package dxn.nl.file.center.netty.consumer;

import dxn.nl.file.center.netty.GzipCodec;
import dxn.nl.file.client.model.codec.MessageCodec;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextClosedEvent;
import org.springframework.stereotype.Component;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class ConsumerNettyServer implements CommandLineRunner, ApplicationListener<ContextClosedEvent> {
    @Value("${cms.export.server.dealPort}")
    private int port;

    private EventLoopGroup bossGroup;
    private EventLoopGroup workerGroup;
    public Channel channel;

    public void start() throws InterruptedException {
        ConsumerSocketManager.centPort = port;
        /**
         * 创建两个线程组 bossGroup 和 workerGroup
         * bossGroup 只是处理连接请求，真正的和客户端业务处理，会交给 workerGroup 完成
         *  两个都是无线循环
         */
        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        try {
            //创建服务器端的启动对象，配置参数
            ServerBootstrap bootstrap = new ServerBootstrap();
            //设置两个线程组
            bootstrap.group(bossGroup, workerGroup)
                    //使用NioServerSocketChannel 作为服务器的通道实现
                    .channel(NioServerSocketChannel.class)
                    //设置线程队列得到连接个数
                    .option(ChannelOption.SO_BACKLOG, 10240)
                    //设置保持活动连接状态
                    .childOption(ChannelOption.SO_KEEPALIVE, true)
                    //通过NoDelay禁用Nagle,使消息立即发出去，不用等待到一定的数据量才发出去
                    .childOption(ChannelOption.TCP_NODELAY, true)
                    //可以给 bossGroup 加个日志处理器
                    .handler(new LoggingHandler(LogLevel.INFO))
                    //给workerGroup 的 EventLoop 对应的管道设置处理器
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        //给pipeline 设置处理器
                        @Override
                        protected void initChannel(SocketChannel socketChannel) {
                            ChannelPipeline pipeline = socketChannel.pipeline();
                            pipeline.addLast(new GzipCodec());

                            //固定帧长解码器
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4));
                            pipeline.addLast(new LengthFieldPrepender(4));
                            pipeline.addLast(new ChunkedWriteHandler());
                            pipeline.addLast("ping", new IdleStateHandler(60, 30, 60 * 10, TimeUnit.SECONDS));

                            //自定义协议编解码器ServerApplication
                            pipeline.addLast(new MessageCodec());
                            pipeline.addLast( new ConsumerNettyServerHandler());
                        }
                    });

            DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            //启动服务器并绑定一个端口并且同步生成一个 ChannelFuture 对象
            ChannelFuture cf = bootstrap.bind(port).sync();
            if (cf.isSuccess()) {
                log.info("导出服务监听启动成功");
            }
        } finally {
        }
    }

    //@Override
    public void destroy() throws Exception {
        try {
            log.info("destroy开始停止导出任务");
            if (channel != null && channel.isActive()) {
                channel.close();
            }

            //发送异常关闭
            if (bossGroup != null) {
                bossGroup.shutdownGracefully();
            }
            if (workerGroup != null) {
                workerGroup.shutdownGracefully();
            }
        }
        catch (Exception ex)
        {
            log.error("",ex);
        }
    }

    @Override
    public void run(String... args) throws Exception {
        start();
    }

    @Override
    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
        try {
            log.info("onApplicationEvent开始停止导出任务");
            destroy();
            log.info("onApplicationEvent开始停止导出任务");
        } catch (Exception e) {
        }
    }
}